#include "ecouteursourisgraphicsview.h"
#include "fenetreimage.h"
#include "ui_fenetreimage.h"

EcouteurSourisGraphicsView::EcouteurSourisGraphicsView(QWidget * fenetre)
{
    this->modeDeTraitement  = EcouteurSourisGraphicsView::NO_ACTION;
    this->fenetreImage      = (FenetreImage*)fenetre;
    this->dragging          = false;

    QObject::connect(fenetreImage->getMaScene(), SIGNAL(mouseMove(QPoint)), this, SLOT(traitementMoveAndDrag(QPoint)));
    QObject::connect(fenetreImage->getMaScene(), SIGNAL(mousePress(QPoint)), this, SLOT(traitementPress(QPoint)));
    QObject::connect(fenetreImage->getMaScene(), SIGNAL(mouseRelease(QPoint)), this, SLOT(traitementRelease(QPoint)));
}

EcouteurSourisGraphicsView::~EcouteurSourisGraphicsView(){
}

void EcouteurSourisGraphicsView::setMode(int mode){
    this->modeDeTraitement  = mode;
}


/************ PRESS **************/

void EcouteurSourisGraphicsView::traitementPress(QPoint event){
    this->dragging  = true;
    switch(this->modeDeTraitement){
    case FUSION:
        this->actionPressFusion(&event);
        break;
    case CROP:
        this->actionPressCrop(&event, QString("Crop"));
        break;
    case CRAYON:
        this->actionPressCrayon(&event);
    case DEPLACEMENT:
        this->actionPressDeplacement(&event);
        break;
    case SELRECT:
        this->actionPressCrop(&event, QString("Selection rectangulaire"));
        break;
    }
}

void EcouteurSourisGraphicsView::actionPressFusion(QPoint *event){
    this->actionPressDeplacement(event);
}

void EcouteurSourisGraphicsView::actionPressCrop(QPoint* event, QString titre){

    Calque *copie = fenetreImage->getCalqueCourant()->copie();
    //on met a jour l'historique TODO problème
    ElementHistorique* eh = new ElementHistorique(titre, copie);
    fenetreImage->historique.ajouter(eh);

    QImage::Format format= QImage::Format_ARGB32;

    this->imageCourante = this->fenetreImage->getCalqueCourant();
    this->xDep  = event->x()- this->imageCourante->getPosX();
    this->yDep  = event->y()- this->imageCourante->getPosY();

    fenetreImage->ajouterCalque(new Calque(fenetreImage->getMaScene()->width(), fenetreImage->getMaScene()->height(),format, 0, 0, 0));
}

void EcouteurSourisGraphicsView::actionPressCrayon(QPoint *event){

    this->xDep  = event->x()- fenetreImage->getCalqueCourant()->getPosX();
    this->yDep  = event->y()- fenetreImage->getCalqueCourant()->getPosY();

}

void EcouteurSourisGraphicsView::actionPressDeplacement(QPoint*  event){
    QGraphicsItem* item = fenetreImage->getGraphItemCourant();
    xDep   = event->x()-item->pos().rx();
    yDep   = event->y()-item->pos().ry();
}


/************ MOVE AND DRAG *************/

void EcouteurSourisGraphicsView::traitementMoveAndDrag(QPoint event){
    if(this->dragging){
        switch(this->modeDeTraitement){
        case FUSION:
            this->actionDragFusion(&event);
            break;
        case CROP:
            this->actionDragCrop(&event);
            break;
        case CRAYON:
            this->actionDragCrayon(&event);
            break;
        case DEPLACEMENT:
            this->actionDragDeplacement(&event);
            break;
        case SELRECT:
            this->actionDragCrop(&event);
            break;
        }
    }else{
        switch(this->modeDeTraitement){
        case PIPETTE:
            this->actionMovePipette(event.x(), event.y());
            break;
        }
    }
}

void EcouteurSourisGraphicsView::actionMovePipette(int x, int y){
    Calque* image = this->fenetreImage->getCalqueCourant();
    this->xDep = x -image->getPosX() ;
    this->yDep =  y-image->getPosY();
    if(xDep>=0 && xDep<image->width() && yDep >=0 && yDep < image->height()){
        QRgb couleur = this->fenetreImage->getCalqueCourant()->pixel(xDep, yDep);
        this->fenetreImage->afficheCouleur(couleur);
    }
}

void EcouteurSourisGraphicsView::actionDragFusion(QPoint* event){
    this->actionDragDeplacement(event);
}

void EcouteurSourisGraphicsView::actionDragCrop(QPoint* event){
    //on dessine la selection

    int x, y, tailleX, tailleY;
    x   = std::min(this->xDep+this->imageCourante->getPosX(), event->x());
    y   = std::min(this->yDep+this->imageCourante->getPosY(), event->y());

    tailleX = abs(this->xDep - event->x() + this->imageCourante->getPosX());
    tailleY = abs(this->yDep - event->y() + this->imageCourante->getPosY());


    Calque * transparent    = this->fenetreImage->getCalqueCourant();
    QRgb couleur;
    // on vide l'image transparente
    for(int i=0; i<transparent->height(); i++){
        for(int j=0; j<transparent->width(); j++){
            couleur = transparent->pixel(j, i);
            if(qAlpha(couleur) != 0){
                couleur = qRgba(qRed(couleur), qGreen(couleur), qBlue(couleur), 0);
                transparent->setPixel(j, i, couleur);
            }
        }
    }
    QPainter painter(transparent);

    painter.drawRect(x,y,tailleX,tailleY);

    painter.end();

    this->fenetreImage->updateCalqueCourant();
}

void EcouteurSourisGraphicsView::actionDragCrayon(QPoint* event){

    Calque * image    = this->fenetreImage->getCalqueCourant();
    QPainter painter(image);
    painter.setPen(this->fenetreImage->getMaMainWindow()->couleur->getCouleur());
    painter.drawLine(this->xDep, this->yDep, event->x()-image->getPosX(), event->y()-image->getPosY());
    painter.end();
    this->xDep  = event->x()-image->getPosX();
    this->yDep  = event->y()-image->getPosY();

    this->fenetreImage->updateCalqueCourant();
}

void EcouteurSourisGraphicsView::actionDragDeplacement(QPoint* event){
    int posX   = event->x();
    int posY   = event->y();

    QGraphicsItem* item = fenetreImage->getGraphItemCourant();
    item->setPos(posX-xDep, posY-yDep);



    // on prend l'image
    //  Calque * image    = this->fenetreImage->getCalqueCourant();

    // on la deplace
//    this->selection->setPosition(event->pos().x() - this->xDep,
//                                 event->pos().y() - this->yDep);

//    this->fenetreImage->updateCalqueCourant();
}


/************ REALEASE *************/

void EcouteurSourisGraphicsView::traitementRelease(QPoint event){
    this->dragging  = false;

    switch(this->modeDeTraitement){
    case PIPETTE:
        this->actionReleasePipette(&event);
        break;
    case FUSION:
        this->actionReleaseFusion(&event);
        break;
    case CROP:
        this->actionReleaseCrop(&event);
        break;
    case CRAYON:
        this->actionReleaseCrayon(&event);
        break;
    case DEPLACEMENT:
        this->actionReleaseDeplacement();
        break;
    case CISEAUX:
        this->actionReleaseCiseaux(&event);
        break;
    case SELRECT:
        this->actionReleaseSelRect(&event);
        break;
    }
}

void EcouteurSourisGraphicsView::actionReleasePipette(QPoint* ){
    //on relache la pipette
    this->fenetreImage->getMaMainWindow()->getUi()->actionPipette->setChecked(false);
    this->fenetreImage->getMonUi()->VueImage->setCursor(Qt::ArrowCursor);
    this->modeDeTraitement  = NO_ACTION;
}

void EcouteurSourisGraphicsView::actionReleaseFusion(QPoint* ){
    //on met a jour l'historique
    Calque *copie = fenetreImage->getCalqueCourant()->copie();
    ElementHistorique* eh = new ElementHistorique(QString("Fusion"), copie);
    fenetreImage->historique.ajouter(eh);
}

void EcouteurSourisGraphicsView::actionReleaseCrop(QPoint* event){
    // on supprime le calque transparent
    this->fenetreImage->supprimerCalqueCourant();
    this->imageCourante = this->fenetreImage->getCalqueCourant();

    int xFin, yFin;
    int tmp;

    this->xDep  = std::min(this->imageCourante->width()-1, std::max(0, this->xDep));
    this->yDep  = std::min(this->imageCourante->height()-1, std::max(0, this->yDep));
    xFin        = std::min(this->imageCourante->width()-1, std::max(0, event->x()-this->imageCourante->getPosX()));
    yFin        = std::min(this->imageCourante->height()-1, std::max(0, event->y()-this->imageCourante->getPosY()));

    // on inverse les valeurs au besoin
    if(this->xDep > xFin){
        tmp         = xFin;
        xFin        = this->xDep;
        this->xDep  = tmp;
    }
    if(this->yDep > yFin){
        tmp         = yFin;
        yFin        = this->yDep;
        this->yDep  = tmp;
    }

    Calque * image = fenetreImage->getCalqueCourant();

    QImage *copie = new QImage(image->copy(this->xDep, this->yDep, xFin-this->xDep, yFin-this->yDep));

    //on ne garde que le bout croppé
    this->fenetreImage->changerCalqueCourant(new Calque(*copie, 0, 0));
    this->fenetreImage->a_ete_modifie = true;
    this->fenetreImage->updateCalqueCourant();
}

void EcouteurSourisGraphicsView::actionReleaseCrayon(QPoint* event){
    Calque * image    = this->fenetreImage->getCalqueCourant();
    QPainter painter(image);
    painter.setPen(this->fenetreImage->getMaMainWindow()->couleur->getCouleur());
    this->fenetreImage->a_ete_modifie =true;
    painter.drawLine(this->xDep, this->yDep, event->x()-image->getPosX(), event->y()-image->getPosY());
    painter.end();

    this->fenetreImage->a_ete_modifie = true;
    this->fenetreImage->updateCalqueCourant();
}

void EcouteurSourisGraphicsView::actionReleaseDeplacement(){
    //on met a jour l'historique
    Calque *copie = fenetreImage->getCalqueCourant()->copie();
    ElementHistorique* eh = new ElementHistorique(QString("Deplacement Calque"), copie);
    fenetreImage->historique.ajouter(eh);
    //this->selection->setPosition(event->pos().x() - this->xDep,
                           //      event->pos().y() - this->yDep);
    //this->fenetreImage->updateCalqueCourant();
    this->fenetreImage->a_ete_modifie = true;

    QGraphicsItem* item = fenetreImage->getGraphItemCourant();
    this->fenetreImage->getCalqueCourant()->setPosition(item->pos().x(),item->pos().y());
}

void EcouteurSourisGraphicsView::actionReleaseCiseaux(QPoint* event){
    Calque * image    = this->fenetreImage->getCalqueCourant();

    // position sur image
    this->xDep = event->x()-image->getPosX();
    this->yDep = event->y()-image->getPosY();

    // si dans l'image
    if(this->xDep>=0 && this->xDep<image->width()
            && this->yDep>=0 && this->yDep<image->height()){

        // on garde en memoire l'ancienne image
        Calque* memoire = image->copie();
        ElementHistorique* eh = new ElementHistorique(QString("Ciseaux"), memoire);
        fenetreImage->historique.ajouter(eh);

        // on fait l'action desiree
        Matrices * selection    = new Matrices(image->height(), image->width());
            selection->remplirMatriceDe(0);
        Matrices * marques      = new Matrices(image->height(), image->width());
            marques->remplirMatriceDe(0);

        EcouteurSourisGraphicsView::ciseauxAnnexe(image, marques, selection, this->yDep, this->xDep, image->pixel(this->xDep, this->yDep));

        bool ok;
        for(int i=0; i<image->height(); i++){
            for(int j=0; j<image->width(); j++){
                ok = false;
                if(selection->getValeurDansCase(i, j)){
                    if(selection->isPositionValide(i-1, j) && !selection->getValeurDansCase(i-1, j)){
                        ok = true;
                        image->setPixel(j, i-1, qRgb(255,255,255));
                    }
                    if(selection->isPositionValide(i, j-1) && !selection->getValeurDansCase(i, j-1)){
                        ok = true;
                        image->setPixel(j-1, i, qRgb(255,255,255));
                    }
                    if(selection->isPositionValide(i, j+1) && !selection->getValeurDansCase(i, j+1)){
                        ok = true;
                        image->setPixel(j+1, i, qRgb(255,255,255));
                    }
                    if(selection->isPositionValide(i+1, j) && !selection->getValeurDansCase(i+1, j)){
                        ok = true;
                        image->setPixel(j, i+1, qRgb(255,255,255));
                    }

                    if(ok){
                        image->setPixel(j, i, qRgb(0,0,0));
                    }
                }
            }
        }

        this->fenetreImage->a_ete_modifie = true;
        this->fenetreImage->updateCalqueCourant();
    }

}

void EcouteurSourisGraphicsView::ciseauxAnnexe(Calque * image, Matrices * marques, Matrices * selection, int i, int j, QRgb couleurDepart, int n){

    if(n>50000){
        return;
    }

    QRgb couleur = image->pixel(j, i);

    // si les conditions sont verifiees
    if(EcouteurSourisGraphicsView::isConcordant(couleurDepart, couleur)){
        // on garde le point
        selection->setValeurDansCase(i, j, 1);

        // on verifie sur ses voisins si ils existent et s'ils ne sont pas encore testes
        if(marques->isPositionValide(i-1, j) && !marques->getValeurDansCase(i-1, j)){
            i--;
            // dans ce cas, on les marque
            marques->setValeurDansCase(i, j, 1);
            // et on les teste
            EcouteurSourisGraphicsView::ciseauxAnnexe(image, marques, selection, i, j, couleurDepart, ++n);
        }
        if(marques->isPositionValide(i, j-1) && !marques->getValeurDansCase(i, j-1)){
            j--;
            // dans ce cas, on les marque
            marques->setValeurDansCase(i, j, 1);
            // et on les teste
            EcouteurSourisGraphicsView::ciseauxAnnexe(image, marques, selection, i, j, couleurDepart, ++n);
        }
        if(marques->isPositionValide(i, j+1) && !marques->getValeurDansCase(i, j+1)){
            j++;
            // dans ce cas, on les marque
            marques->setValeurDansCase(i, j, 1);
            // et on les teste
            EcouteurSourisGraphicsView::ciseauxAnnexe(image, marques, selection, i, j, couleurDepart, ++n);
        }
        if(marques->isPositionValide(i+1, j) && !marques->getValeurDansCase(i+1, j)){
            i++;
            // dans ce cas, on les marque
            marques->setValeurDansCase(i, j, 1);
            // et on les teste
            EcouteurSourisGraphicsView::ciseauxAnnexe(image, marques, selection, i, j, couleurDepart, ++n);
        }
    }
}

bool EcouteurSourisGraphicsView::isConcordant(QRgb couleurDepart, QRgb couleurATestee){
    int diffMax = 25;

    int rd = qRed(couleurDepart);
    int rt = qRed(couleurATestee);
    int vd = qGreen(couleurDepart);
    int vt = qGreen(couleurATestee);
    int bd = qBlue(couleurDepart);
    int bt = qBlue(couleurATestee);

    int i = 0;

    if(rd-diffMax <= rt && rt <= rd+diffMax){
        i++;
    }
    if(vd-diffMax <= vt && vt <= vd+diffMax){
        i++;
    }
    if(bd-diffMax <= bt && bt <= bd+diffMax){
        i++;
    }
    return i>1;
}

void EcouteurSourisGraphicsView::actionReleaseSelRect(QPoint *event){

    int xFin, yFin;
    int tmp;

    this->xDep  = std::min(this->imageCourante->width()-1, std::max(0, this->xDep));
    this->yDep  = std::min(this->imageCourante->height()-1, std::max(0, this->yDep));
    xFin        = std::min(this->imageCourante->width()-1, std::max(0, event->x()-this->imageCourante->getPosX()));
    yFin        = std::min(this->imageCourante->height()-1, std::max(0, event->y()-this->imageCourante->getPosY()));

    // on inverse les valeurs au besoin
    if(this->xDep > xFin){
        tmp         = xFin;
        xFin        = this->xDep;
        this->xDep  = tmp;
    }
    if(this->yDep > yFin){
        tmp         = yFin;
        yFin        = this->yDep;
        this->yDep  = tmp;
    }

    Calque * transparent = fenetreImage->getCalqueCourant();
    QRgb couleur;

    for(int i=0; i<transparent->height(); i++){
        for(int j=0; j<transparent->width(); j++){
            //si dans les bornes de la selection
            if(yDep<=i && i<=yFin && xDep<=j && j<=xFin){
                couleur = this->imageCourante->pixel(j, i);

                transparent->setPixel(j, i, couleur);
                this->imageCourante->setPixel(j, i, qRgba(255, 255, 255, qAlpha(couleur)));
            }else if(qAlpha(transparent->pixel(j, i)) != 0){
                transparent->setPixel(j, i, qRgba(255, 255, 255, 0));
            }
        }
    }

    transparent = transparent->copie();

    //on ne garde que le bout croppé
    this->fenetreImage->supprimerCalqueCourant();
    this->fenetreImage->a_ete_modifie = true;
    this->fenetreImage->updateCalqueCourant();
    this->fenetreImage->ajouterCalque(transparent);
}
